package com.star.common.oss.service.impl;

import com.amazonaws.ClientConfiguration;
import com.amazonaws.Protocol;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.*;
import com.star.common.oss.exception.OssException;
import com.star.common.oss.model.FileUploadStatusVO;
import com.star.common.oss.model.OssStsVO;
import com.star.common.oss.model.OssTemporaryUrlVO;
import com.star.common.oss.properties.OssProperties;
import com.star.common.oss.service.AbstractOssClient;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;

/**
 * aws oss处理类
 *
 * @author zhouhengzhe #公众号：小白的Java进阶之路
 * @version 1.0
 * @date 2023/6/27 14:10
 */
public class AwsOssClient extends AbstractOssClient {
    private final AmazonS3 s3client;

    public AwsOssClient(OssProperties.AwsOssProperties awsOssProperties) {
        super.checkAllPropertiesNotNull(awsOssProperties);
        AwsClientBuilder.EndpointConfiguration endpointConfig =
                new AwsClientBuilder.EndpointConfiguration(awsOssProperties.getEndPoint()
                        , Region.getRegion(Regions.CN_NORTH_1).getName());

        AWSCredentials awsCredentials = new BasicAWSCredentials(awsOssProperties.getAccessKey(), awsOssProperties.getSecretKey());
        AWSCredentialsProvider awsCredentialsProvider = new AWSStaticCredentialsProvider(awsCredentials);
        ClientConfiguration clientConfig = new ClientConfiguration();
        clientConfig.setProtocol(Protocol.HTTP);
        this.s3client = AmazonS3Client.builder()
                .withEndpointConfiguration(endpointConfig)
                .withClientConfiguration(clientConfig)
                .withCredentials(awsCredentialsProvider)
                .disableChunkedEncoding()
                .withPathStyleAccessEnabled(true)
                .withForceGlobalBucketAccessEnabled(true)
                .build();
    }

    /**
     * 获取临时访问凭证
     *
     * @param sessionName   SessionName的长度和规则，不要有空格，只能有'-' '_' 字母和数字等字符
     * @param bucketName    oss桶 例如 ldstore
     * @param regionId      RAM的地域ID。以华东1（杭州）地域为例，regionID填写为cn-hangzhou。也可以保留默认值，默认值为空字符串（""）。
     * @param stsApiVersion sts版本
     * @param roleArn       获取的角色ARN
     * @param policyJson    比如以下Policy用于限制仅允许使用临时访问凭证向目标存储空间examplebucket下的src目录上传文件。
     *                      临时访问凭证最后获得的权限是步骤4设置的角色权限和该Policy设置权限的交集，即仅允许将文件上传至目标存储空间examplebucket下的src目录。
     *                      如果policy为空，则用户将获得该角色下所有权限。
     *                      {\n" +
     *                      "    \"Version\": \"1\", \n" +
     *                      "    \"Statement\": [\n" +
     *                      "        {\n" +
     *                      "            \"Action\": [\n" +
     *                      "                \"oss:PutObject\"\n" +
     *                      "            ], \n" +
     *                      "            \"Resource\": [\n" +
     *                      "                \"acs:oss:*:*:examplebucket/src/*\" \n" +
     *                      "            ], \n" +
     *                      "            \"Effect\": \"Allow\"\n" +
     *                      "        }\n" +
     *                      "    ]\n" +
     *                      "}
     * @return 临时访问凭证
     */
    @Override
    public OssStsVO getSts(String sessionName,
                           String bucketName,
                           String regionId,
                           String stsApiVersion,
                           String roleArn,
                           String policyJson) {
        throw new OssException("Features are not under development get sts by aws");
    }

    /**
     * 获取临时url
     *
     * @param contentType  请求头
     * @param bucketName   桶名
     * @param objectKey    文件名
     * @param documentType 文件类型 例如 jpg
     * @return
     */
    @Override
    public OssTemporaryUrlVO getTemporaryUrl(String contentType, String bucketName, String objectKey, String documentType) {
        throw new OssException("Features are not under development get temporary url by aws");
    }

    /**
     * 文件上传
     *
     * @param uploadFile 上传的文件
     * @param fileName   文件名称
     * @param bucketName 桶名称
     * @return
     */
    @Override
    public String uploadFile(MultipartFile uploadFile, String fileName, String bucketName) {
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentType(uploadFile.getContentType());
        objectMetadata.setContentLength(uploadFile.getSize());
        try {
            PutObjectResult putObjectResult = s3client.putObject(
                    new PutObjectRequest(bucketName,
                            fileName,
                            uploadFile.getInputStream(),
                            objectMetadata)
                            .withCannedAcl(CannedAccessControlList.PublicRead)
            );
            if (Objects.isNull(putObjectResult)) {
                throw new OssException("The return value for uploaded files is null");
            }
            //第一种方式
            return s3client.getUrl(bucketName, fileName).toString();
        } catch (Exception e) {
            throw new OssException("upload file fail");
        }
    }

    /**
     * 文件流上传
     *
     * @param fileName    文件名称
     * @param bucketName  桶名称
     * @param out         文件流
     * @param contentType 文件类型
     * @return
     */
    @Override
    public String uploadFileByte(String fileName, String bucketName, byte[] out, String contentType) {
        ByteArrayInputStream bis = new ByteArrayInputStream(out);
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentType(contentType);
        metadata.setContentLength(bis.available());

        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, fileName, bis, metadata);
        putObjectRequest.setCannedAcl(CannedAccessControlList.PublicRead);
        PutObjectResult putObjectResult = s3client.putObject(putObjectRequest);
        if (Objects.isNull(putObjectResult)) {
            throw new OssException("upload file fail");
        }
        return s3client.getUrl(bucketName, fileName).toString();
    }

    /**
     * 获取文件上传进度
     *
     * @param fileName   文件名称
     * @param bucketName 桶名称
     * @return 返回文件上传进度
     */
    @Override
    public FileUploadStatusVO getFileUploadPlan(String fileName, String bucketName) {
        throw new OssException("Features are not under development file upload plan by aws");
    }

    /**
     * 文件下载
     *
     * @param response   响应
     * @param fileName   文件名称
     * @param bucketName 桶名称
     */
    @Override
    public void fileDownload(HttpServletResponse response, String fileName, String bucketName) {
        InputStream inputStream = null;
        try {
            S3Object s3Object = s3client.getObject(bucketName, fileName);
            inputStream = s3Object.getObjectContent();
            response.setContentType(s3Object.getObjectMetadata().getContentType());
            response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
            byte[] buffer = new byte[4096];
            int bytesRead = -1;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                response.getOutputStream().write(buffer, 0, bytesRead);
            }
        } catch (Exception e) {
            throw new OssException("download file fail");
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            try {
                if (response.getOutputStream() != null) {
                    response.getOutputStream().flush();
                    response.getOutputStream().close();
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 删除文件
     *
     * @param fileName   文件名称
     * @param bucketName 桶名称
     */
    @Override
    public void deleteFile(String fileName, String bucketName) {
        s3client.deleteObject(bucketName, fileName);
    }
}
